﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;

namespace editor.cfg
{

    public abstract class TType
    {
        protected TType(Dictionary<string, string> attrs)
        {
            Attrs = attrs;
        }

        public virtual bool IsPrimitive()
        {
            return false;
        }

        public Dictionary<string, string> Attrs { get; private set; }

        public abstract string Desc { get; }

        public static string[] GetSubArray(string[] arr, int from)
        {
            var dst = new string[arr.Length - from];
            for (var i = from; i < arr.Length; i++)
            {
                dst[i - from] = arr[i];
            }
            return dst;
        }

        public static TType Create(Defines ds, string module, string[] types, Dictionary<string, string> attrs)
        {
            Contract.Requires(types.Length > 0);
            var type = types[0];
            switch (type)
            {
                case "bool":
                    Contract.Requires(types.Length == 1);
                    return new TBool(attrs);
                case "byte":
                case "short":
                case "int":
                case "sint":
                case "fint":
                    Contract.Requires(types.Length == 1);
                    return new TInt(attrs);
                case "long":
                case "slong":
                case "flong":
                    Contract.Requires(types.Length == 1);
                    return new TLong(attrs);
                case "float":
                case "double":
                    return new TFloat(attrs);
                case "octets":
                case "string":
                    Contract.Requires(types.Length == 1);
                    return new TString(attrs);
                case "array":
                case "list":
                    Contract.Requires(types.Length > 1);
                    return new TList(Create(ds, module, GetSubArray(types, 1), attrs), attrs);
                case "bytes":
                    return new TList(new TInt(attrs), attrs);
                case "set":
                    Contract.Requires(types.Length > 1);
                    return new TSet(Create(ds, module, GetSubArray(types, 1), attrs), attrs);
                case "map":
                    Contract.Requires(types.Length > 2);
                    return new TMap(Create(ds, module, new string[] { types[1] }, TMap.GetKeyAttrs(attrs)), Create(ds, module, GetSubArray(types, 2), TMap.GetValueAttrs(attrs)), attrs);
                default:
                    {
                        Contract.Requires(types.Length == 1);

                        if (!ds.Exist(type) && !type.Contains("."))
                        {
                            type = module + "." + type;
                            if (!ds.Exist(type))
                                Err($"module:{module} type:{type} not exist");
                        }
                        if (ds.Enums.ContainsKey(type))
                            return new TEnum(ds.Enums[type], attrs);
                        return new TBean(ds.Beans[type], attrs);
                    }
            }
        }

        public static void Err(string msg)
        {
            throw new Exception(msg);
        }

        public abstract DData New(XmlElement ele);

        public abstract DData NewDefault();
    }

    public class TBool : TType
    {
        public TBool(Dictionary<string, string> attrs) : base(attrs)
        {

        }
        public override bool IsPrimitive()
        {
            return true;
        }
        public override string Desc { get { return "布尔值(bool)"; } }

        public override DData New(XmlElement ele)
        {
            return new DBool(this, bool.Parse(ele.InnerText));
        }

        public override DData NewDefault()
        {
            return new DBool(this, false);
        }
    }

    public class TString : TType
    {
        public TString(Dictionary<string, string> attrs) : base(attrs)
        {

        }
        public override bool IsPrimitive()
        {
            return true;
        }
        public override DData New(XmlElement ele)
        {
            return new DString(this, ele.InnerText);
        }

        public override DData NewDefault()
        {
            return new DString(this, "");
        }
        public override string Desc { get { return "字符串(string)"; } }
    }

    public class TInt : TType
    {
        public TInt(Dictionary<string, string> attrs) : base(attrs)
        {

        }
        public override bool IsPrimitive()
        {
            return true;
        }
        public override DData New(XmlElement ele)
        {
            return new DInt(this, int.Parse(ele.InnerText));
        }

        public override DData NewDefault()
        {
            return new DInt(this, 0);
        }
        public override string Desc { get { return "整数(int)"; } }
    }

    public class TLong : TType
    {
        public TLong(Dictionary<string, string> attrs) : base(attrs)
        {

        }
        public override bool IsPrimitive()
        {
            return true;
        }
        public override DData New(XmlElement ele)
        {
            return new DLong(this, long.Parse(ele.InnerText));
        }

        public override DData NewDefault()
        {
            return new DLong(this, 0L);
        }
        public override string Desc { get { return "长整数(long)"; } }
    }

    public class TEnum : TType
    {
        public TEnum(ENUM e, Dictionary<string, string> attrs) : base(attrs)
        {
            Type = e;
        }
        public ENUM Type { get; private set; }

        public override DData New(XmlElement ele)
        {
            return new DEnum(this, ele.InnerText);
        }
        public override bool IsPrimitive()
        {
            return true;
        }
        public override DData NewDefault()
        {
            return new DEnum(this, Type.Cases[0]);
        }
        public override string Desc { get { return $"枚举(enum:{Type.FullName})"; } }
    }

    public class TFloat : TType
    {
        public TFloat(Dictionary<string, string> attrs) : base(attrs)
        {

        }
        public override bool IsPrimitive()
        {
            return true;
        }
        public override DData New(XmlElement ele)
        {
            var x = ele.InnerText;
            if (string.IsNullOrWhiteSpace(x))
                x = "0";
            return new DFloat(this, x);
        }

        public override DData NewDefault()
        {
            return new DFloat(this, "0");
        }
        public override string Desc { get { return "浮点数(float)"; } }
    }

    public class TBean : TType
    {
        public TBean(Bean bean, Dictionary<string, string> attrs) : base(attrs)
        {
            BeanType = bean;
        }

        public Bean BeanType { get; private set; }
        public override string Desc { get { return $"结构(bean:{BeanType.FullName})"; } }


        private List<DData> ReadFields(Bean bean, XmlElement ele)
        {
            var fields = new List<DData>();

            foreach (var f in bean.HierarchyFields)
            {
                DData newField = null;
                var find = false;
                //var idStr = f.Id.ToString();
                foreach (XmlElement e in ele)
                {
                    if (e.Name == f.Name) // || e.GetAttribute("id") == idStr)
                    {
                        newField = f.FType.New(e as XmlElement);
                        find = true;
                        break;
                    }
                }
                if (!find)
                {
                    newField = f.FType.NewDefault();
                }
                newField.Field = f;
                fields.Add(newField);
            }
            return fields;
        }

        public override DData New(XmlElement ele)
        {
            if (BeanType.IsDynamic)
            {
                var className = ele.GetAttribute("type");
                if (!BeanType.Children.Any(c => c.SimpleName == className))
                {
                    Err($"class:<{className}> isn't bean:{BeanType.FullName}'s sub type");
                }

                var bean = BeanType.Children.FirstOrDefault(c => c.SimpleName == className);
                if (bean == null)
                {
                    Err($"class:<{className}> isn't bean:{BeanType.FullName}'s sub type");
                }
                return new DDynamicBean(this, new DBean(new TBean(bean, Attrs), ReadFields(bean, ele)));
            }
            else
            {
                return new DBean(this, ReadFields(BeanType, ele));
            }

        }

        private static Random random = new Random();

        private static int newDepth = 0;
        public override DData NewDefault()
        {
            if (BeanType.IsDynamic)
            {
                Bean bean = null;
                var children = BeanType.Children;
                if (newDepth == 0)
                {
                    bean = children[0];
                }
                else
                {
                    bean = BeanType.Children.FirstOrDefault(b => b.HierarchyFields.All(f =>
                    {
                        var ftype = f.FType;
                        return !((ftype is TBean) || (ftype is TList) || (ftype is TSet) || (ftype is TMap));
                    }));
                    if (bean == null)
                    {
                        bean = BeanType.Children[random.Next() % BeanType.Children.Count];
                    }
                }

                ++newDepth;
                try
                {
                    var fields = bean.HierarchyFields.Select(f =>
                    {
                        var d = f.FType.NewDefault();
                        d.Field = f;
                        return d;
                    }).ToList();
                    return new DDynamicBean(this, new DBean(new TBean(bean, Attrs), fields));
                }
                finally
                {
                    --newDepth;
                }
            }
            else
            {
                var fields = BeanType.HierarchyFields.Select(f =>
                {
                    var d = f.FType.NewDefault();
                    d.Field = f;
                    return d;
                }).ToList();
                return new DBean(this, fields);
            }
        }
    }

    public class TList : TType
    {

        public TList(TType valuetype, Dictionary<string, string> attrs) : base(attrs)
        {
            ValueType = valuetype;
        }

        public TType ValueType { get; private set; }
        public override string Desc { get { return $"列表(list:{ValueType.Desc})"; } }

        public override DData New(XmlElement ele)
        {
            var datas = new ObservableCollection<DData>();

            foreach (XmlNode e in ele)
            {
                if (!(e is XmlElement)) continue;
                datas.Add(ValueType.New(e as XmlElement));
            }

            return new DList(this, datas);
        }

        public override DData NewDefault()
        {
            return new DList(this, new ObservableCollection<DData>());
        }
    }

    public class TSet : TType
    {

        public TSet(TType valuetype, Dictionary<string, string> attrs) : base(attrs)
        {
            ValueType = valuetype;
        }

        public TType ValueType { get; private set; }
        public override string Desc { get { return $"集合(set:{ValueType.Desc})"; } }

        public override DData New(XmlElement ele)
        {
            var datas = new ObservableCollection<DData>();
            foreach (XmlNode e in ele)
            {
                if (!(e is XmlElement)) continue;
                datas.Add(ValueType.New(e as XmlElement));
            }
            return new DSet(this, datas);
        }

        public override DData NewDefault()
        {
            return new DSet(this, new ObservableCollection<DData>());
        }
    }

    public class TMap : TType
    {
        public TMap(TType keyType, TType valueType, Dictionary<string, string> attrs) : base(attrs)
        {
            KeyType = keyType;
            ValueType = valueType;
        }

        public TType KeyType { get; private set; }
        public TType ValueType { get; private set; }
        public override string Desc { get { return $"映射(map: {KeyType.Desc} => {ValueType.Desc})"; } }

        public override DData New(XmlElement ele)
        {
            var datas = new ObservableCollection<KeyValuePair<DData, DData>>();
            foreach (XmlNode e in ele)
            {
                if (!(e is XmlElement)) continue;

                DData key = null;
                DData value = null;
                foreach (XmlNode n in e)
                {
                    switch (n.Name)
                    {
                        case "key":
                            {
                                key = KeyType.New(n as XmlElement);
                                break;
                            }
                        case "value":
                            {
                                value = ValueType.New(n as XmlElement);
                                break;
                            }
                    }
                }
                if (key == null || value == null)
                    Err("map key or value missing");
                datas.Add(new KeyValuePair<DData, DData>(key, value));
            }
            return new DMap(this, datas);
        }


        public static Dictionary<string, string> GetKeyAttrs(Dictionary<string, string> attrs)
        {
            var newAttrs = new Dictionary<string, string>();
            foreach (var e in attrs)
            {
                var strs = e.Value.Split(':');
                newAttrs[e.Key] = strs.Length > 0 ? strs[0] : string.Empty;
            }
            return newAttrs;
        }

        public static Dictionary<string, string> GetValueAttrs(Dictionary<string, string> attrs)
        {
            var newAttrs = new Dictionary<string, string>();
            foreach (var e in attrs)
            {
                var strs = e.Value.Split(':');
                newAttrs[e.Key] = strs.Length > 1 ? strs[1] : string.Empty;
            }
            return newAttrs;
        }

        public override DData NewDefault()
        {
            return new DMap(this, new ObservableCollection<KeyValuePair<DData, DData>>());
        }
    }
}
